/*********************************************************************
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2013, PAL Robotics, S.L.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of the PAL Robotics nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *********************************************************************/

/*
 * Author: Luca Marchionni
 * Author: Bence Magyar
 * Author: Enrique Fernández
 * Author: Paul Mathieu
 * Author: Luke Liao
 */

#ifndef ODOMETRY_H_
#define ODOMETRY_H_

#include <ros/time.h>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/rolling_mean.hpp>
#include <boost/function.hpp>

#include <ros/ros.h>
namespace omni_triangle_controller
{
namespace bacc = boost::accumulators;

/**
 * \brief The Odometry class handles odometry readings
 * (2D pose and velocity with related timestamp)
 */
class Odometry
{
public:

	/// Integration function, used to integrate the odometry:
	typedef boost::function<void(double, double, double)> IntegrationFunction;

	/**
	 * \brief Constructor
	 * Timestamp will get the current time value
	 * Value will be set to zero
	 * \param velocity_rolling_window_size Rolling window size used to compute the velocity mean
	 */
	Odometry(size_t velocity_rolling_window_size = 10);

	/**
	 * \brief Initialize the odometry
	 * \param time Current time
	 */
	void init(const ros::Time &time);

	/**
	 * \brief Updates the odometry class with latest wheels position
	 * \param wheel1_vel  1  wheel1 velocity[rad]
	 * \param wheel2_vel  1  wheel2 velocity[rad]
	 * \param wheel3_vel  1  wheel3 velocity[rad]
	 * \return true if the odometry is actually updated
	 */
	bool update(double wheel1_vel, double wheel2_vel, double wheel3_vel, const ros::Time &time);


	/**
	 * \brief heading getter
	 * \return heading [rad]
	 */
	double getHeading() const
	{
		return heading_;
	}

	/**
	 * \brief x position getter
	 * \return x position [m]
	 */
	double getX() const
	{
		return x_;
	}

	/**
	 * \brief y position getter
	 * \return y position [m]
	 */
	double getY() const
	{
		return y_;
	}

	/**
	 * \brief linear x velocity getter
	 * \return linear velocity [m/s]
	 */
	double getLinearX() const
	{
		return linear_x_;
	}

	/**
	 * \brief linear y velocity getter
	 * \return linear velocity [m/s]
	 */
	double getLinearY() const
	{
		return linear_y_;
	}
	/**
	 * \brief angular velocity getter
	 * \return angular velocity [rad/s]
	 */
	double getAngular() const
	{
		return angular_;
	}

	/**
	 * \brief Sets the wheel parameters: radius and separation
	 * \param wheel_separation Seperation between left and right wheels [m]
	 * \param wheel_radius     Wheel radius [m]
	 */
	void setWheelParams(double wheel_center, double wheel_radius);

private:

	/// Rolling mean accumulator and window:
	typedef bacc::accumulator_set<double, bacc::stats<bacc::tag::rolling_mean> > RollingMeanAcc;
	typedef bacc::tag::rolling_window RollingWindow;

	/**
	 * \brief Integrates the velocities (linear and angular) using 2nd order Runge-Kutta
	 * \param linear  Linear_x  velocity   [m] (linear  displacement, i.e. m/s * dt) computed by encoders
	 * \param linear  Linear_y  velocity   [m] (linear  displacement, i.e. m/s * dt) computed by encoders
	 * \param angular Angular velocity [rad] (angular displacement, i.e. m/s * dt) computed by encoders
	 */
	void integrateRungeKutta2(double linear_x, double linear_y, double angular);

	/**
	 * \brief Integrates the velocities (linear and angular) using exact method
	 * \param linear  Linear  velocity   [m] (linear  displacement, i.e. m/s * dt) computed by encoders
	 * \param linear  Linear_y  velocity   [m] (linear  displacement, i.e. m/s * dt) computed by encoders
	 * \param angular Angular velocity [rad] (angular displacement, i.e. m/s * dt) computed by encoders
	 */
	void integrateExact(double linear_x, double linear_y, double angular);

	/// Current timestamp:
	ros::Time timestamp_;

	/// Current pose:
	double x_;        //   [m]
	double y_;        //   [m]
	double heading_;  // [rad]

	/// Current velocity:
	double linear_x_;  //   [m/s]
	double linear_y_;
	double angular_; // [rad/s]

	/// Wheel kinematic parameters [m]:
	double wheel_center_;
	double wheel_radius_;

	/// Previou wheel position/state [rad]:
	double wheel1_pos_;
	double wheel2_pos_;
	double wheel3_pos_;

	/// Rolling mean accumulators for the linar and angular velocities:
	size_t velocity_rolling_window_size_;
	RollingMeanAcc linear_x_acc_;
	RollingMeanAcc linear_y_acc_;
	RollingMeanAcc angular_acc_;

	/// Integration funcion, used to integrate the odometry:
	IntegrationFunction integrate_fun_;
};
}

#endif /* ODOMETRY_H_ */
